
#include <vector>
#include <unordered_set>
#include "Intaddr.h"
#include "Mutex.h"

namespace zmh
{
    class KeyToInt // 将Key转为整形
    {
    public:
        int operator()(const Intaddr &addr) const
        {
            return std::hash<std::string>()(addr.Name());
        }
    };

    class Router
    {
    public:
        // 又引发了一个新的问题,死锁问题
        void Register(const Intaddr &user)
        {
            auto_Mutex guard_mutex(_mutex_Regist); // Register与Cancel之间,不会出现资源并发访问问题
            {
                auto_Mutex guard_mutex1(_mutex_Capacity_login);
                _login.emplace_back(user);
            }
        }
        void Cancel(const Intaddr &user)
        {
            auto_Mutex guard_mutex(_mutex_Cancel);
            {
                auto_Mutex guard_mutex1(_mutex_Capacity_cancel);
                _cancel.emplace_back(user);
            }
        }
        void Route(std::string mes, int id)
        {
            auto_Mutex guard_mutex(_mutex_Route);

            {
                // 不会出现死锁,没有哪个线程,需要同时竞争相同的两把锁的
                auto_Mutex guard_mutex1(_mutex_Capacity_login); // 同时申请锁,保证版本一致
                auto_Mutex guard_mutex2(_mutex_Capacity_cancel);
                for (auto x : _login)
                    _addr.insert(x);
                for (auto x : _cancel)
                    _addr.erase(x);
            }

            for (const auto &x : _addr)
            {
                if (sendto(id, mes.c_str(), mes.size(), 0, (struct sockaddr *)&x, sizeof(x)) == -1)
                {
                    LOG(zmh::LogLevel::ERROR) << "send mistake:" << strerror(errno);
                    exit(errno);
                }
            }
        }

    private:
        std::unordered_set<Intaddr, KeyToInt> _addr; // 存储聊天室的套接字
        std::vector<Intaddr> _login;                 // 存储聊天室的套接字
        std::vector<Intaddr> _cancel;                // 存储聊天室的套接字
        Mutex _mutex_Regist;                         // for insert --- 限制了进入该函数的线程只能有一个(少了)
        Mutex _mutex_Cancel;                         // for cancel
        Mutex _mutex_Route;                          // for route
        Mutex _mutex_Capacity_login;                 // for _login --- 限制了访问该类型资源的线程只能有一个(更少了)
        Mutex _mutex_Capacity_cancel;                // for _cancel
    };
}